package org.eoti.ai.genetic.gp.example1;

import org.eoti.ai.genetic.gp.*;

import java.math.BigInteger;
import java.util.Comparator;

/**
 * The MIT License
 * <p/>
 * Copyright (c) 2009 Malachi de AElfweald (malachid@gmail.com)
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
public class ExampleFitness
extends Fitness<BigInteger, BigInteger>
{
	private static final BigInteger invalid = BigInteger.valueOf(Integer.MAX_VALUE);
	private BigInteger goal;
	private Comparator<BigInteger> fitnessComparator;
	private Comparator<Program<BigInteger,BigInteger>> programComparator;
	private Criteria<BigInteger,BigInteger> criteria;

	public ExampleFitness(BigInteger goal, Criteria<BigInteger,BigInteger> criteria)
	{
		this.goal = goal;
		this.criteria = criteria;
		this.fitnessComparator = new FitnessComparator();
		this.programComparator = new ProgramComparator();
	}

	public BigInteger getGoal(){
		return goal;
	}

	public void setGoal( BigInteger goal ){
		this.goal = goal;
	}

	public Comparator<BigInteger> getFitnessComparator(){
		return fitnessComparator;
	}

	public Comparator<Program<BigInteger, BigInteger>> getProgramComparator(){
		return programComparator;
	}

	/**
	 * if the program is not an acceptable size, returns Integer.MAX_VALUE
	 * @returns abs(offset from goal)
	 */
	public BigInteger getFitness(Program<BigInteger, BigInteger> program)
	{
		BigInteger pre = Variable.get( "example1", "x" );
		// testing the concept of a program returning a list of values
		for(int x=0; x<100; x++)
		{
			Variable.set( "example1", "x", BigInteger.valueOf(x) );
			BigInteger _fitness = _getFitness(program);
			if(_fitness.equals( BigInteger.ZERO ))
				return BigInteger.ZERO;

//			System.out.format("_fitness != 0: %s\n", _fitness);
		}
		Variable.set("example1", "x", pre);
		return _getFitness(program);
	}

	private BigInteger _getFitness(Program<BigInteger,BigInteger> program)
	{
		try{
			int size = program.getRootNode().totalSize();
			if(size < criteria.getMinProgramSize()) return invalid;
			if(size > criteria.getMaxProgramSize()) return invalid;
			BigInteger value = program.execute();
			return value.subtract(goal).abs();
		}catch(ProgramException e){
			return invalid;
		}
	}

	class FitnessComparator
	implements Comparator<BigInteger>
	{
		public int compare( BigInteger fitness1, BigInteger fitness2 ){
			return fitness1.compareTo(fitness2);
		}
	}

	class ProgramComparator
	implements Comparator<Program<BigInteger,BigInteger>>
	{
		public int compare( Program<BigInteger, BigInteger> program1, Program<BigInteger, BigInteger> program2 )
		{
			return fitnessComparator.compare(getFitness(program1), getFitness(program2) );
		}
	}
}
